# coding=utf-8

from ..ToolsConfig import *
from ..ItemTools import IsSameItem, GetItemBaseInfoSafely, GetItemDurabilityPercent, DeclareItemAnyNum
from ..ScreenTools import ScreenTools
from BlueFoxSystem import BlueFoxClientSystem
from ..OtherTools import Mount, FixList
from ControllableUi.Animation import MoveToPos


class SlotOffset(dict):
    def __getitem__(self, item):
        return super(SlotOffset, self).get(item, 0)


@Mount(BlueFoxClientSystem)
class RucksackScreen(baseScreen):
    flyingItemRender = None
    slotOffset = SlotOffset()
    flyingMoveControl = None  # type:MoveToPos
    blueFoxClient = BlueFoxClientSystem if 0 else None

    def __init__(self, name_spase, ui_key, param, items=None, customData=None, flyingPanelPath=None):
        super(RucksackScreen, self).__init__(name_spase, ui_key, param)
        self.selectSlot = -1
        self.__itemNum = []  # 物品数量
        self.__itemD = []  # (目前,总) 耐久度
        self.__itemId = []  # 物品ID
        self.__isItemNone = []  # 是否为缺失物品
        self.flyingItem = {}  # 跟随鼠标的物品
        self.moveControls = []
        self.exchangeLock = False
        self.items = items  # 所有物品
        if self.items is None: self.items = []
        self.customData = customData  # 自定义数据
        self.gridIndex = {}
        self.flyingPanelPath = flyingPanelPath
        self.offsetSlot = {}

    def GetStartAndFinalIndex(self, collectName):
        """
        获取起止索引段
        :param collectName:
        :return:
        """
        offsetKey = self.offsetSlot.keys()
        offsetKey.sort()
        offset = self.slotOffset[collectName]
        if offset == offsetKey[-1]: return offset, len(self.items)
        for index in range(len(offsetKey) - 1):
            if offset == offsetKey[index]: return offset, offsetKey[index + 1]

    def RecalculateOffsetSlot(self):
        """
        重新计算OffsetSlot
        :return:
        """
        self.offsetSlot = {}
        for _ in self.slotOffset.items(): self.offsetSlot[_[1]] = _[0]

    def PrintSlotName(self):
        offsetKey = self.offsetSlot.keys()
        offsetKey.sort()
        for _ in offsetKey:
            print (_, self.offsetSlot[_])
        print '--------------'

    def AddSlot(self, slotName, gridPath, items, dim, refresh=True):
        """
        增加槽位，需要在Create函数执行之前绑定所需的connectionName
        :param slotName: 槽位名称
        :param refresh: 是否刷新数据
        :param gridPath: 网格路径
        :param items: 物品集
        :param dim: 网格尺寸
        :return:
        """
        if slotName in self.slotOffset: return
        self.InitSlot(slotName, gridPath, items, dim)
        self.InitItemData() if refresh else 0

    def DelSlot(self, slotName, refresh=True):
        """
        删除槽位，包括items，slotOffset，offsetSlot
        :param slotName:槽位名称
        :param refresh: 是否刷新数据
        :return:
        """
        if not slotName in self.slotOffset: return
        offsetKey = self.offsetSlot.keys()
        offsetKey.sort()
        startAndEndIndex = self.GetStartAndFinalIndex(slotName)
        oldOffset = startAndEndIndex[0]
        size = startAndEndIndex[1] - startAndEndIndex[0]
        del self.items[startAndEndIndex[0]: startAndEndIndex[1]]
        del self.offsetSlot[oldOffset]
        del self.slotOffset[slotName]
        del self.gridIndex[slotName]
        for index in offsetKey[::-1]:
            if oldOffset < index: self.slotOffset[self.offsetSlot[index]] = index - size
        self.RecalculateOffsetSlot()
        self.InitItemData() if refresh else 0

    # ------------------------------ init ------------------------------
    def InitItemDataOne(self, slot, item):
        """
        对某个槽位进行初始化，需要在InitData之后调用
        """
        if item:
            itemInfo = GetItemBaseInfoSafely(item)
            self.__itemNum[slot] = item['count']
            self.__itemId[slot] = itemInfo['id_aux']
            self.__itemD[slot] = GetItemDurabilityPercent(item, itemInfo)
            self.__isItemNone[slot] = False
        else:
            self.__itemNum[slot] = 0
            self.__itemD[slot] = (0.0, 0.0)
            self.__itemId[slot] = 0
            self.__isItemNone[slot] = False

    def InitItemData(self):
        """
        依据当前items初始化显示所需的物品数据
        :return:
        """
        self.moveControls = [None for _ in self.items]
        self.__itemNum = [0 for _ in self.items]
        self.__itemD = [(0.0, 0.0) for _ in self.items]
        self.__itemId = [0 for _ in self.items]
        self.__isItemNone = [False for _ in self.items]
        for index in range(self.items.__len__()): self.InitItemDataOne(index, self.items[index])

    def InitFlyingPanel(self, path):
        self.flyingItemRender = self.GetBaseUIControl(path).asItemRenderer()
        self.flyingMoveControl = MoveToPos(self.flyingItemRender, 0.1)

    def InitSlot(self, slotName, gridPath, items, dimension):
        """
        初始化物品槽
        :param dimension: 物品槽网格的尺寸
        :param items: 物品槽内的物品
        :param slotName:物品槽名称
        :param gridPath:物品槽的网格路径
        :return:
        """
        self.GetBaseUIControl(gridPath).asGrid().SetGridDimension(tuple(dimension))
        self.gridIndex[slotName] = gridPath
        offset = len(self.items)
        self.slotOffset[slotName] = offset
        self.offsetSlot[offset] = slotName
        self.items += items
        self.BindingAll(slotName)

    # ------------------------------ init ------------------------------

    def PlayerInvItemChanged(self, args):
        """
        玩家背包服务端主动发生改变时触发
        """
        self.SetUiVisibleInfo(args['item'], args['slot'])

    def Create(self):
        self.blueFoxClient.ListenForEvent(
            MOD_NAME, 'BlueFoxServerSystem', 'PlayerInvItemChanged', self, self.PlayerInvItemChanged
        )

    def Destroy(self):
        self.blueFoxClient.UnListenForEvent(
            MOD_NAME, 'BlueFoxServerSystem', 'PlayerInvItemChanged', self, self.PlayerInvItemChanged
        )
        self.CreateItemEntityIfFlyItem()
        self.blueFoxClient.NotifyToServer('RucksackScreenClosed', {'customData': self.customData})
        for moveControl in self.moveControls: moveControl.Finish() if moveControl else 0

    def ItemCellGridInit(self, gridPath, collectionName):
        """
        物品槽网格初始化时触发
        :param gridPath:网格路径
        :param collectionName:集合名称
        :return:
        """
        pass

    def BindingAll(self, collectionName):
        bindingFunc = ScreenTools.CollectionViewBinder
        bindingBool = viewBinder.BF_BindBool
        bindingInt = viewBinder.BF_BindInt
        bindingFloat = viewBinder.BF_BindFloat
        bindingString = viewBinder.BF_BindString
        bindingFunc(bindingBool, collectionName, '#item_num_visible', self.ItemsNumVisible)  # 是否显示物品数量文字
        bindingFunc(bindingString, collectionName, '#item_num', self.ItemsNum)  # 显示物品数量
        bindingFunc(bindingBool, collectionName, '#select_visible', self.SelectVisible)  # 是否显示物品槽选择
        bindingFunc(bindingInt, collectionName, '#item_id_aux', self.RenderItems)  # 显示物品
        bindingFunc(bindingBool, collectionName, '#item_durability_visible', self.ItemsDurabilityVisible)  # 是否显示物品耐久度条
        bindingFunc(
            bindingFloat, collectionName, '#item_durability_total_amount', self.ItemsDurabilityTotal
        )  # 物品耐久度条总量
        bindingFunc(
            bindingFloat, collectionName, '#item_durability_current_amount', self.ItemsDurabilityCurrent
        )  # 物品耐久度条当前量
        bindingFunc(bindingBool, collectionName, '#item_none_visible', self.ItemsNone)  # 是否显示缺失物品背景
        bindingFunc(bindingBool, collectionName, '#item_cell_init', self.ItemCellInit)  # 初始化时触发

    # --------------------- bindings ---------------------

    def ItemCellInit(self, collectionName, index):
        """
        初始化时触发，无需任何返回值
        :param collectionName:集合名称
        :param index:在网格内的索引值
        :return:
        """
        if not index: self.ItemCellGridInit(self.gridIndex[collectionName], collectionName)

    def ItemsNumVisible(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return self.__itemNum[index] > 1 if index < self.__itemNum.__len__() else False

    def ItemsNum(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return str(self.__itemNum[index]) if index < self.__itemNum.__len__() else ''

    def SelectVisible(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return index == self.selectSlot

    def RenderItems(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return self.__itemId[index] if index < self.__itemId.__len__() else 0

    def ItemsDurabilityVisible(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        if index >= self.__itemD.__len__():
            return False
        info = self.__itemD[index]
        return info[0] != info[1] and info[1] > 0

    def ItemsDurabilityTotal(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return self.__itemD[index][1] if index < self.__itemD.__len__() else 0.0

    def ItemsDurabilityCurrent(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return self.__itemD[index][0] if index < self.__itemD.__len__() else 0.0

    def ItemsNone(self, collectionName, index):
        index = index + self.slotOffset.get(collectionName, 0)
        return self.__isItemNone[index] if index < self.__isItemNone.__len__() else False

    # --------------------- bindings ---------------------

    def SetNoneItem(self, slot, itemDict):
        """
        将某个槽位设置为缺少物品
        :param slot:
        :param itemDict:
        :return:
        """
        itemInfo = GetItemBaseInfoSafely(itemDict)
        self.__itemNum[slot] = itemDict['count']
        self.__itemD[slot] = GetItemDurabilityPercent(itemDict, itemInfo)
        self.__itemId[slot] = itemInfo['id_aux']
        self.__isItemNone[slot] = True
        self.items[slot] = None

    def NoticeServerChangeItem(self, slot, itemDict):
        """
        通知服务端更新物品
        :param slot:槽位
        :param itemDict:物品字典
        :return:
        """
        self.blueFoxClient.NotifyToServer(
            'ChangeItem', {'slot': slot, 'item': itemDict, 'customData': self.customData}
        )

    def NoticeServerSpawnItemEntities(self, itemDict):
        self.blueFoxClient.NotifyToServer("SpawnItemEntity", {
            'posDim': (
                clientCompFactory.CreatePos(clientApi.GetLocalPlayerId()).GetPos(),
                clientCompFactory.CreateGame(LevelId).GetCurrentDimension()
            ),
            'item': itemDict
        })

    def SetItem(self, slot, itemDict=None):
        """
        设置某个槽位, 不论是否物品为空, 返回原本物品
        :param itemDict:
        :param slot:
        :return:
        """
        return self.SetUiItem(slot, itemDict) if itemDict else self.SetUiItemNone(slot)

    def SetItemAbs(self, slot, itemDict=None):
        """
        设置某个槽位, 不论是否物品为空
        :param itemDict:
        :param slot:
        :return:
        """
        self.SetUiItemAbs(slot, itemDict) if itemDict else self.SetUiItemNone(slot)

    def SetUiItemNone(self, slot):
        """
        将某个槽位设置为空,返回原本物品
        :param slot:
        :return:
        """
        originItem = self.items[slot]
        self.SetUiItemAbs(slot, {})
        return originItem

    def SetUiItemAbs(self, slot, destinationItem, noticeServer=True):
        """
        设置某一个槽位的物品, 不返回任何物品及合并
        :param noticeServer: 是否通知服务端，默认发送
        :param slot:设置槽位
        :param destinationItem:目标物品字典
        :return:
        """
        destinationItemInfo = GetItemBaseInfoSafely(destinationItem)
        self.__itemNum[slot] = destinationItem.get('count', 0)
        self.__itemD[slot] = GetItemDurabilityPercent(destinationItem, destinationItemInfo)
        self.__itemId[slot] = destinationItemInfo.get('id_aux', 0)
        self.__isItemNone[slot] = False

        self.items[slot] = destinationItem
        self.NoticeServerChangeItem(slot, destinationItem) if noticeServer else 0

    def SetUiItem(self, slot, destinationItem):
        """
        设置某一个槽位的物品,返回原本物品如果不可合并或者合并后多出的物品
        此接口会发送ChangeItem事件
        :param slot:设置槽位
        :param destinationItem:目标物品字典
        :return:
        """
        originItem = self.items[slot]
        originItemInfo = GetItemBaseInfoSafely(destinationItem)
        if IsSameItem(originItem, destinationItem):
            if originItem['count'] + destinationItem['count'] <= originItemInfo['maxStackSize']:
                destinationItem['count'] += originItem['count']
                originItem = {}
            else:
                originItem['count'] += destinationItem['count'] - originItemInfo['maxStackSize']
                destinationItem['count'] = originItemInfo['maxStackSize']
        self.SetUiItemAbs(slot, destinationItem)
        return originItem

    def SetUiVisibleInfo(self, itemDict, slot):
        """
        设置物品,通常用于服务端主动设置物品,此接口不会发送ChangeItem事件
        :param itemDict:物品信息字典
        :param slot:设置槽位
        :return:
        """
        self.SetUiItemAbs(slot, itemDict, False)

    def GetOffsetNameByIndex(self, index):
        """
        通过index获取偏移名称
        :param index:
        :return:
        """
        offsetKey = self.offsetSlot.keys()
        offsetKey.sort()
        offset = offsetKey[-1]
        for _ in range(offsetKey.__len__() - 1):
            if offsetKey[_] <= index < offsetKey[_ + 1]:
                return self.offsetSlot[offsetKey[_]]
        return self.offsetSlot[offset]

    def GetOffsetStartIndex(self, index):
        """
        通过index获取偏移起始index
        :param index:
        :return:
        """
        offsetKey = self.offsetSlot.keys()
        offsetKey.sort()
        offset = offsetKey[-1]
        for _ in range(offsetKey.__len__() - 1):
            if offsetKey[_] <= index < offsetKey[_ + 1]:
                return offsetKey[_]
        return offsetKey[-1]

    def GetItemCellByIndex(self, index):
        """
        通过槽位获取itemCell的路径
        :param index:槽位编号
        :return:
        """
        offset = self.GetOffsetStartIndex(index)
        return self.gridIndex[self.offsetSlot[offset]] + '/item_cell{}'.format(index - offset + 1)

    def FlyingMoveFinish(self, slot):
        """
        pe下槽位发生物品堆叠时, flyRender动画完成时触发
        :return:
        """
        self.flyingItemRender.SetVisible(False) if self.flyingItemRender else 0
        self.flyingItem = self.SetItem(slot, self.flyingItem)

    def ExchangeFinish(self, slot, destinationSlot):
        """
        pe下槽位发生物品交换时, 动画完成时触发
        :param destinationSlot: 与其交换的槽位
        :param slot: 槽位
        :return:
        """
        deSlotName = self.GetOffsetNameByIndex(destinationSlot)
        deSlotOffset = destinationSlot - self.GetOffsetStartIndex(destinationSlot)

        originItem = self.items[slot]
        destinationItem = self.items[destinationSlot]
        self.SetItemAbs(slot, destinationItem)
        deSlot = self.slotOffset[deSlotName] + deSlotOffset
        if deSlotName not in self.slotOffset or deSlot >= len(self.items):
            self.flyingItem = originItem
            self.CreateItemEntityIfFlyItem()
        else:
            self.SetItemAbs(deSlot, originItem)
        self.flyingItem = {}

    def StackFinish(self, slot, destinationSlot):
        """
        pe下槽位发生物品堆叠时, slot动画完成时触发
        :param slot:  槽位
        :param destinationSlot: 与其交换的槽位
        :return:
        """
        orSlotName = self.GetOffsetNameByIndex(slot)
        orSlotOffset = slot - self.GetOffsetStartIndex(slot)

        self.flyingItem = self.SetItem(destinationSlot, self.items[slot])
        orSlot = self.slotOffset[orSlotName] + orSlotOffset
        if orSlotName not in self.slotOffset or orSlot >= len(self.items):
            self.CreateItemEntityIfFlyItem()
            self.flyingItem = {}
        else:
            self.SetItemAbs(slot, self.flyingItem)

    @staticmethod
    def SetUiItemForItemRender(render, itemDict):
        """
        使用itemDict设置物品渲染
        :param render:itemRender
        :param itemDict:需要设置的物品字典
        :return:
        """
        render.SetUiItem(
            itemDict['newItemName'], itemDict['newAuxValue'],
            (itemDict.get('enchantData', []) + itemDict.get('modEnchantData', [])).__len__() > 0,
            itemDict.get('userData', None)
        )

    def PcDown(self, selectSlot):
        """
        电脑点击物品槽触发
        :param selectSlot:槽位
        :return:
        """
        self.flyingItem = self.SetItem(selectSlot, self.flyingItem)
        (self.SetUiItemForItemRender(self.flyingItemRender, self.flyingItem) if self.flyingItemRender else 0) \
            if self.flyingItem else self.flyingItemRender.SetUiItem('minecraft:air', 0)

    def GetSlotAnimationState(self, originSlot, destinationSlot):
        """
        pe模式下, 交换物品时根据槽位确认动画形式
        :param originSlot:先前选择槽位
        :param destinationSlot:后选择槽位
        :return:先前, 后选择, flyingItem
        """
        originItem = self.items[originSlot]
        destinationItem = self.items[destinationSlot]
        itemInfo = GetItemBaseInfoSafely(originItem)
        if IsSameItem(originItem, destinationItem):
            if originItem['count'] + destinationItem['count'] < itemInfo['maxStackSize']:
                return True, False, False
            elif destinationItem['count'] == itemInfo['maxStackSize']:
                return True, True, False
            else:
                return False, False, True
        return True, True, False

    def GetPeFlyingItem(self, originSlot, destinationSlot):
        """
        在pe模式下, 获取flyingItem
        :param originSlot: 现物品槽位
        :param destinationSlot: 目标物品槽位
        :return:
        """
        itemDict1, itemDict2 = DeclareItemAnyNum(self.items[originSlot], 64 - self.items[destinationSlot]['count'])
        self.SetItemAbs(originSlot, itemDict1)
        return itemDict2

    def DeliverItem(self, originSlot, destinationSlot):
        """
        以动画形式从origin向destination传递物品
        :param originSlot:起源物品槽位
        :param destinationSlot:目标物品槽位
        :return:
        """
        ox, oy = self.GetAbsPos(self.GetItemCellByIndex(originSlot) + '/item_cell/item_renderer')
        dx, dy = self.GetAbsPos(self.GetItemCellByIndex(destinationSlot) + '/item_cell/item_renderer')
        _, __, ___ = self.GetSlotAnimationState(originSlot, destinationSlot)
        if _:
            self.moveControls[originSlot].StartMove(
                (dx - ox, dy - oy), self.ExchangeFinish if __ else self.StackFinish, originSlot, destinationSlot
            )
        if __:
            self.moveControls[destinationSlot].StartMove(
                (-dx + ox, -dy + oy), lambda: None
            )
        if ___:
            self.flyingItem = self.GetPeFlyingItem(originSlot, destinationSlot)
            self.SetUiItemForItemRender(self.flyingItemRender, self.flyingItem) if self.flyingItemRender else 0
            self.flyingItemRender.SetVisible(True) if self.flyingItemRender else 0
            self.flyingMoveControl.StartMove((dx, dy), self.FlyingMoveFinish, destinationSlot)

    def PeDown(self, selectSlot):
        """
        手机点击物品槽触发
        :param selectSlot:槽位
        :return:
        """
        if self.selectSlot == selectSlot:
            self.selectSlot = -1
            return
        else:
            if self.selectSlot is not -1:
                if self.items[self.selectSlot]:
                    self.DeliverItem(self.selectSlot, selectSlot)
                    self.selectSlot = -1
                    return
            if self.items[selectSlot]:
                self.selectSlot = selectSlot
                self.flyingItemRender.SetPosition(
                    self.GetAbsPos(self.GetItemCellByIndex(selectSlot) + '/item_cell/item_renderer')
                ) if self.flyingItemRender else 0
                self.flyingMoveControl.ResetInitialPos()

    @viewBinder.binding(viewBinder.BF_ButtonClickUp, '#item_cell_touch')
    def ButtonDown(self, args):
        """
        物品槽被点击会触发无论电脑或者手机
        :param args:与普通的按钮回调参数一致
        :return:
        """
        touchIndex = args['#collection_index'] + self.slotOffset.get(args['#collection_name'], 0)
        self.CreateItemRenderMoveControl(touchIndex)
        self.PeDown(touchIndex) if Platform else self.PcDown(touchIndex)

    def CreateItemRenderMoveControl(self, slot):
        """
        创建物品渲染的移动动画控制器
        :param slot:
        :return:
        """
        if self.moveControls[slot] is None:
            itemCell = self.GetBaseUIControl(self.GetItemCellByIndex(slot))
            self.moveControls[slot] = MoveToPos(
                itemCell.GetChildByName('item_cell').GetChildByName('item_renderer'), 0.1
            )

    def GetAbsPos(self, path):
        """
        获取元素的绝对坐标
        :param path:路径
        :return:绝对坐标, xy
        """
        control = self.GetBaseUIControl(path)
        return control.GetGlobalPosition() if control else (0, 0)

    @viewBinder.binding(viewBinder.BF_BindInt, '#main.gametick')
    def GameTick(self):
        if clientCompFactory and not Platform and self.flyingItemRender:
            x, y = clientCompFactory.CreateActorMotion(clientApi.GetLocalPlayerId()).GetMousePosition()
            self.flyingItemRender.SetPosition((x - 9, y - 9))
            self.flyingItemRender.SetVisible(self.flyingItem)
        for moveControl in self.moveControls:
            moveControl.Refresh() if moveControl else 0
        self.flyingMoveControl.Refresh() if self.flyingMoveControl else 0

    def CreateItemEntityIfFlyItem(self):
        """
        创建跟随鼠标的物品
        :return:
        """
        if self.flyingItem:
            self.NoticeServerSpawnItemEntities(self.flyingItem)
